#include "limit_resources_manager.h"

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include <assert.h>

#define MAX_LIMIT_RESOURCES_TYPE  1024

struct _limit_resources_manager_;
struct _limit_resources_ {
	uint64_t id;
	void * res;
	union {
		struct _limit_resources_ * next;
		void * ctx;
	};
};

struct _limit_resources_manager_ {
	struct _limit_resources_ * free_resources;
	struct _limit_resources_ * resources_mem_ptr;;
	struct resources_opt opt;
	void * ctx;
	int max;
};

////////////////////////////////////////////////////////////////////////////////
// start of base function

#define LINK_TO_LIST(head, ptr) \
	do { \
		(ptr)->next = head; \
		(head) = (ptr); \
	} while(0) 


static struct _limit_resources_manager_ * alloc_resource_manager()
{
	struct _limit_resources_manager_* manager =
		(struct _limit_resources_manager_*)malloc(sizeof(struct _limit_resources_manager_));
	printf("create manager %p\n", manager);
	return manager;
}

static void free_resource_manager(struct _limit_resources_manager_ * manager) 
{
	if (manager) {
		printf("free manager %p\n", manager);
		free(manager);
	}
}
static int init_manager(struct _limit_resources_manager_ * manager,
		int max,
		struct resources_opt * opt,
		void * ctx)
{
	printf("init manager %p\n", manager);

	int i;
	if (manager == 0 || max <= 0) {
		return -1;
	}

	manager->free_resources = 0;
	manager->max = 0;
	memset(&manager->opt, 0, sizeof(manager->opt));
	manager->ctx = 0;
	manager->resources_mem_ptr = (struct _limit_resources_*)malloc(sizeof(struct _limit_resources_) * max);
	if (manager->resources_mem_ptr == 0) {
		return -1;
	}

	manager->max = max;
	if (opt) {
		manager->opt = *opt;
	}
	manager->ctx = ctx;

	for(i = 0; i < max; i++) {
		struct _limit_resources_ * res = manager->resources_mem_ptr + i;
		res->id = i;
		if (res->id == INVALIDATE_ID)  {
			res->id += max;
		}

		res->res = 0;
		if (manager->opt.alloc) {
			res->res = manager->opt.alloc(ctx);
			printf("alloced resources %p(%p) of %p\n", res, res->res, manager);
		}
		res->ctx = ctx;

		LINK_TO_LIST(manager->free_resources, res);
	}
	return 0;
}

static void destroy_resource_manager(struct _limit_resources_manager_ * manager)
{
	if (manager == 0) return;

	printf("destory manager %p\n", manager);
	int i;
	if (manager->resources_mem_ptr) {
		if (manager->opt.free) {
			for(i = 0; i < manager->max; i++) {
				struct _limit_resources_ * res = manager->resources_mem_ptr+i;
				printf("free resources %p(%p) of %p\n", res, res->res, manager);
				manager->opt.free(res->res, manager->ctx);
			}
		}
	}

	manager->max = 0;
	manager->free_resources = 0;
	free(manager->resources_mem_ptr);
	memset(&manager->opt, 0, sizeof(manager->opt));
	manager->ctx = 0;
}

static struct _limit_resources_ * get_free_resources_of_manager(
		struct _limit_resources_manager_ * manager,
		void * ctx)
{
	if(manager == 0 || manager->free_resources == 0) {
		return 0;
	}

	struct _limit_resources_ * resources = manager->free_resources;
	manager->free_resources = resources->next;
	resources->ctx = ctx;

	printf("init resources %p(%p) of %p\n", resources, resources->res, manager);
	if (manager->opt.init) {
		manager->opt.init(resources->res, resources->ctx);
	}
	return resources;
}

static struct _limit_resources_ * get_resources_of_manager_by_id (
		struct _limit_resources_manager_ * manager,
		resid_t resources_id)
{
	if (manager == 0 || resources_id == INVALIDATE_ID) {
		return 0;
	}

	resid_t slot = resources_id % manager->max;
	if (manager->resources_mem_ptr[slot].id == resources_id) {
		return manager->resources_mem_ptr + slot;
	}
	return 0;
}

static void free_resources_of_manager(
		struct _limit_resources_manager_ * manager,
		struct _limit_resources_ * resources)
{
	if (manager == 0 || resources == 0) {
		return;
	}

	printf("destory resources %p(%p) of %p\n", resources, resources->res, manager);
	if (manager->opt.destory) {
		manager->opt.destory(resources->res, resources->ctx);
	}
	resources->id += manager->max;
	resources->ctx = 0;
	resources->next = manager->free_resources;
	manager->free_resources = resources;
}

// end of base function
////////////////////////////////////////////////////////////////////////////////

static struct _limit_resources_manager_ * g_top_manager = 0;

static void * cb_alloc_resource_manager(void*ctx)
{
	return alloc_resource_manager();
}

static void cb_free_resources_manager(void * res, void*ctx)
{
	struct _limit_resources_manager_ * manager = (struct _limit_resources_manager_ *)res;
	free_resource_manager(manager);
}

struct _manager_create_info_ {
	int max;
	struct resources_opt opt;
	void * ctx;
};

static int cb_init_resource_manager(void * res, void*ctx)
{
	struct _limit_resources_manager_ * manager = (struct _limit_resources_manager_ *)res;
	struct _manager_create_info_ * info = (struct _manager_create_info_*)ctx;
	return init_manager(manager, info->max, &info->opt, info->ctx);
}

static void cb_destory_resource_manager(void * res, void*ctx)
{
	struct _limit_resources_manager_ * manager = (struct _limit_resources_manager_ *)res;
	//struct _manager_craete_info_ * info = (struct _manager_craete_info_*)ctx;
	//printf("free _manager_craete_info_ %p\n", info);
	//free(info);
	destroy_resource_manager(manager);
}

resid_t create_resources_manager(int max,
		struct resources_opt * opt,
		void * ctx)
{
	if (g_top_manager == 0) {
		g_top_manager = alloc_resource_manager();
		printf("g_top_manager = %p\n",g_top_manager);
		
		struct resources_opt g_opt;
		g_opt.alloc = cb_alloc_resource_manager;
		g_opt.free = cb_free_resources_manager;
		g_opt.init = cb_init_resource_manager;
		g_opt.destory = cb_destory_resource_manager;

		if (init_manager(g_top_manager, MAX_LIMIT_RESOURCES_TYPE, &g_opt, 0) != 0) {
			free(g_top_manager);
			g_top_manager = 0;
			return INVALIDATE_ID;
		}
	}


	struct _manager_create_info_ sInfo;
	struct _manager_create_info_ * info = &sInfo; //malloc(sizeof(struct _manager_create_info_));
	//printf("alloc _manager_craete_info_ %p\n", info);
	info->max = max;
	info->opt = *opt;
	info->ctx  = ctx;
	struct _limit_resources_ * resources = get_free_resources_of_manager(g_top_manager, info);
	return resources->id;
}

static  struct _limit_resources_manager_ * get_resources_manager_by_id(resid_t manager_id)
{
	struct _limit_resources_ * resources = get_resources_of_manager_by_id(g_top_manager, manager_id);
	if (resources == 0) {
		return 0;
	}

	struct _limit_resources_manager_ * manager = (struct _limit_resources_manager_*)(resources->res);
	return manager;
}

static  struct _limit_resources_ * get_resources_by_two_id(resid_t manager_id, resid_t resources_id) 
{
	struct _limit_resources_manager_ * manager = get_resources_manager_by_id(manager_id);
	if (manager == 0) {
		return 0;
	}

	void  * data = get_resources_of_manager_by_id(manager, resources_id);
	if (data == 0) {
		return 0;
	}

	struct _limit_resources_ * resources = (struct _limit_resources_*)data;
	return resources;
}

void destory_resources_manager(resid_t manager_id)
{
	struct _limit_resources_manager_ * manager = get_resources_manager_by_id(manager_id);
	if (manager == 0) {
		return;
	}

	struct _limit_resources_ * resources = get_resources_of_manager_by_id(g_top_manager, manager_id);
	if (resources == 0) {
		return ;
	}
	free_resources_of_manager(g_top_manager, resources);
}

resid_t get_free_resource(resid_t manager_id, void * ctx)
{
	struct _limit_resources_manager_ * manager = get_resources_manager_by_id(manager_id);
	if (manager == 0) {
		return INVALIDATE_ID;
	}

	struct _limit_resources_ * resources = get_free_resources_of_manager(manager, ctx);
	if (resources == 0) {
		return INVALIDATE_ID;
	}

	return resources->id;
}

void release_resources(resid_t manager_id, resid_t resources_id)
{
	struct _limit_resources_manager_ * manager = get_resources_manager_by_id(manager_id);
	if (manager == 0) {
		return ;
	}

	struct _limit_resources_ * resources = get_resources_of_manager_by_id(manager, resources_id);
	if (resources == 0) {
		return ;
	}

	free_resources_of_manager(manager, resources);
}

void * get_resources(resid_t manager_id, resid_t resources_id)
{
	struct _limit_resources_ * resources = get_resources_by_two_id(manager_id, resources_id);
	if (resources == 0) {
		return 0;
	}
	return resources->res;
}

void * set_resources(resid_t manager_id, resid_t resources_id, void * res)
{
	struct _limit_resources_ * resources = get_resources_by_two_id(manager_id, resources_id);
	if (resources == 0) {
		return 0;
	}
	void * old_res= resources->res;
	resources->res = res;
	return old_res;
}

void * get_resources_data(resid_t manager_id, resid_t resources_id)
{
	struct _limit_resources_ * resources = get_resources_by_two_id(manager_id, resources_id);
	if (resources == 0) {
		return 0;
	}
	return resources->ctx;
}

void * set_resources_data(resid_t manager_id, resid_t resources_id, void * ctx)
{
	struct _limit_resources_ * resources = get_resources_by_two_id(manager_id, resources_id);
	if (resources == 0) {
		return 0;
	}
	void * old_ctx = resources->ctx;
	resources->ctx = ctx;
	return old_ctx;
}
